package org.autumn.design.proxy.jdk;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @Description:
 * @Author: Zhangyuhan
 * @Date: 2022/4/11
 */
@Slf4j
public class JdkProxyTest {
    // 这个main 可能是你的service 的一个方法你 这个log 那让你统计时间 你怎么办 嗯！你不能每一个方法都打印一遍把 那太麻烦了。。你在写个时间的 在下面
    public static void main(String[] args) {
        // 这个是被代理的
        Movable tank = new Tank();
        // 我们需要在tank移动前后加入日志 你会咋做 那就调用move前加一个 后加一个 不行吗 你写出来 不要改变源代码就是tank的class
        // 这样你的业务就和class 分离了 就没有封装的特性了 java 3大特性
        // Movable logTank = new LogTank(tank);
        // ok? 你会感觉很没用 对吧 yes  这个只是一个简单的例子 所以你感觉不是很深，就是我们不要修改源代码
        // logTank.move();

         // TimeTank timeTank = new TimeTank(logTank);
        // timeTank.move();  //  但是我们不能每添加一个功能就写一个类 所以说我们要用到动态代理来实现 Proxy

            //代理对象的处理程序，我们将要代理的真实对象传入代理对象的调用处理的构造函数中，最终代理对 的调用处理程序会调用真实对象的方法。
        Movable tankProxy = (Movable) Proxy.newProxyInstance(JdkProxyTest.class.getClassLoader(), tank.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invoke = method.invoke(tank, args);
                System.out.println(method.getName());
                return invoke;
            }
        });

        tankProxy.move();
    }
}



    interface Movable {
        void move();
    }


     class Tank implements Movable {
        @Override
        public void move() {
            int time = new Random().nextInt(5);
            System.out.println("time = " + time);
            try {
                TimeUnit.SECONDS.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("tank moving ....");
        }
    }

    class TimeTank implements Movable {

        Movable tank;

        public TimeTank(Movable tank) {
            this.tank = tank;
        }

        @Override
        public void move() {
            long start = System.currentTimeMillis();
            tank.move();
            System.out.println(System.currentTimeMillis() - start);
        }
        // 不要改源码  源码 你写
    }

    class LogTank implements Movable {
        Movable tank;

        public LogTank(Movable tank) {
            this.tank = tank;
        }

        @Override
        public void move() {
            System.out.println("tank move start ...");
            tank.move();
            System.out.println("tank move end ...");
        }
        // ok ? 在这 你写 我没想法 是把上面写的拿到这个类里？？？ 对啊 就是不要改吧源代码 开闭原则
        // 这里实现 继承都行 我就实现接口了 ok？ yes

    }
